#pragma once
#include <iostream>
#include <sys/select.h>
#include <string>
#include "Sock.hpp"
#include "Log.hpp"

const static uint16_t gport = 8888;
typedef int type_t;

class SelectServer
{

    static const int N = sizeof(fd_set) * 8; // 用来管理fd的数组大小

public:
    SelectServer(uint16_t port = gport) : _port(port)
    {
    }

    void InitServer()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        for (int i = 0; i < N; ++i)
        {
            _fdarray[i] = defaultfd;
        }
    }

    void Accepter()
    {
        //std::cout << "有一个新连接到来了" << std::endl;
        // 这个时候再Accept
        std::string clientip;
        uint16_t clientport;
        int sock = _listensock.Accept(&clientip, &clientport);
        if (sock < 0)
        {
            return;
        }
        logMessage(Debug, "[%s:%d], sock: %d", clientip.c_str(), clientport, sock);
        // 同样的我们这时候不能直接对fd直接进行read/recv，因为我们并不清楚此时fd当中是否有数据就绪
        // 因此也是用同样方法将其交给select帮我们等待事件就绪
        int pos = 1;
        for (; pos < N; ++pos)
        {
            if (_fdarray[pos] == defaultfd) // 找到没被占用的位置
            {
                break;
            }
        }
        if (pos >= N) // 判断其是否合法
        {
            close(sock); // 不合法（越界）就代表管理fd的数组已满，无法继续承载，只能将此fd关闭
            logMessage(Warning, "sockfd array[] full");
        }
        else
        {
            _fdarray[pos] = sock; // 合法就写入管理fd的数组
        }
    }

    void HandlerEvent(fd_set &rfds)
    {
        for (int i = 0; i < N; ++i)
        {
            int fd = _fdarray[i];
            if (fd == defaultfd)
            {
                continue;
            }
            if (fd == _listensock.Fd() && FD_ISSET(_listensock.Fd(), &rfds))
            {
                Accepter();
            }
            else if (fd != _listensock.Fd() && FD_ISSET(fd, &rfds))
            {
                // serviceIO()
                char buffer[1024];
                ssize_t s = recv(fd, buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    buffer[s - 2] = '\0';
                    std::cout << "client# " << buffer << std::endl;
                    //发送回去时也要将fd交给select等待写事件就绪
                    std::string echo = buffer;
                    echo += "[select server echo]\n";
                    send(fd, echo.c_str(), echo.size(), 0);
                }
                else 
                {
                    if (s == 0)
                    {
                        logMessage(Info, "client quit..., fdarray[%d] -> defaultfd: %d->%d", i, fd, defaultfd);
                    }
                    else 
                    {
                        logMessage(Info, "recv error, client quit..., fdarray[%d] -> defaultfd: %d->%d", i, fd, defaultfd);
                    }
                    close(fd);
                    _fdarray[i] = defaultfd;
                }
            }
        }
    }

    void Start()
    {
        // _listensock.Accept();
        // 要实现多路复用我们不能直接Accept，一开始服务器里没有新连接到来时Accept会发生阻塞式等待
        // 在网络中，新连接到来被当做读事件就绪
        // 我们需要select来帮我们等待读事件就绪
        // demo0
        _fdarray[0] = _listensock.Fd();
        while (true)
        {
            fd_set rfds;
            FD_ZERO(&rfds);
            int maxfd = _fdarray[0];
            for (int i = 0; i < N; ++i)
            {
                if (_fdarray[i] == defaultfd) // 过滤非法fd
                {
                    continue;
                }
                // 合法fd
                FD_SET(_fdarray[i], &rfds);
                if (maxfd < _fdarray[i])
                {
                    maxfd = _fdarray[i]; // 更新最大fd
                }
            }
            // struct timeval timeout = {0, 0};
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);
            switch (n)
            {
            case 0:
                logMessage(Debug, "timeout %d: %s", errno, strerror(errno));
                break;
            case -1:
                logMessage(Warning, "%d: %s", errno, strerror(errno));
                break;
            default:
                logMessage(Debug, "有一个事件就绪: %d", n);
                HandlerEvent(rfds);
                DebugPrint();
                break;
            }
        }
    }

    void DebugPrint()
    {
        std::cout << "fdarray[]: ";
        for (int i = 0; i < N; ++i)
        {
            if (_fdarray[i] == defaultfd)
            {
                continue;
            }
            std::cout << _fdarray[i] << " ";
        }
        std::cout << std::endl;
    }

    ~SelectServer()
    {
        _listensock.Close();
    }

private:
    uint16_t _port;
    Sock _listensock;
    type_t _fdarray[N]; // 用来管理所有的fd
};